'---------------------------------------------------------------------------------------------------
'TEXT TRANSFORMATION LANGUAGE (TTL)
'---------------------------------------------------------------------------------------------------
'SUMMARY
'   Purpose:  Text Transformation Language is a scripting language that uses interpreted commands to transform text files.
'   Author:   Dustinian Camburides
'   Platform: QB64 (www.qb64.net)
'   Revision: 1.7
'   Updated:  4/3/2012
'---------------------------------------------------------------------------------------------------
'INSTRUCTIONS
'   1. Create a plain-text file of commands per the "syntax" section below.
'   2. Run the TTL intepreter, identifying the script path, input path, and output path as command-line parameters.
'---------------------------------------------------------------------------------------------------
'SYNTAX
'   Modules: Use the below command to include a seperate file of TTL commands into your current script.
'       INCLUDE "c:\example_folder\example_file.ttl"
'   Commands: Use the below commands to transform your input text into your output text.
'       REPLACE "find" WITH "add"
'       REPLACE ALL WITH "add" BETWEEN "precedant" AND "antecedant"
'       REPLACE "find" WITH "add" BETWEEN "precedant" AND "antecedant"
'       REPLACE ALL WITH "add" FROM "precedant" TO "antecedant"
'       REPLACE EVERYTHING AFTER "precedant" TO "antecedant" WITH "add"
'---------------------------------------------------------------------------------------------------
'COMMAND-LINE PARAMETERS
'   TTL.EXE script_path.ttl, input_path.txt, output_path.txt
'---------------------------------------------------------------------------------------------------
'REVISION HISTORY
'   1.7: (in progress)
'        Broke the source code up into modules: [string.manipulation], [string.array], and [text.file.input.output].
'   1.6: Fixed numerous errors in the [string manipulation] section.
'   1.5: Re-named parameters/arguments for logical consistency.
'   1.4: Altered the structure of the "parse" subroutines for better re-usability.
'   1.3: Re-ordered and re-organized procedures for logical consistency.
'   1.2: Added the ability to target any script to any file through a command-line parameter.
'   1.1: Re-ordered and re-organized procedures for logical consistency.
'   1.0: First working version.
'---------------------------------------------------------------------------------------------------
'PLANNED ENHNACEMENTS
'   Minor:
'       - Tokens: Constants for common "white space" characters (TAB, RETURN, etc.).
'       - Wildcards: Symbols for "wildcard" searches (*, #, etc.).
'       - Input Validation: Prevent infinite "Replace All..." command.
'   Major:
'       - Add command: Replace First X after Y with Z.
'       - Target Folder: The ability to target all the files in a folder and its subfolders (filtered by extension).
'       - Append: The ability to append text from another file to the beginning or end of the current file.
'       - Replace All Once / Recursive: The ability to determine whether all instances of a query string are replaced once per initial location in a file... or over and over again until they no longer occur.
'       - Debug Logs: The ability to log transformations in progress at different levels of detail.
'       - Text User-Interface (TUI): A user interface assembled from ASCII characters (similar to the QB IDE).
'       - Graphical User-Interface (GUI): A windows-style user interface (buttons, menus, scroll bars, etc.).
'       - Syntax Definitons: A more elegant way to validate/recognize command syntax (DTD file, XML definitions, etc.).
'---------------------------------------------------------------------------------------------------
'LIBRARIES
'   - string.array.1.0.bas
'   - text.file.input.output.1.1.bas
'   - string.manipulation.1.6.bas
'---------------------------------------------------------------------------------------------------
'PROCEDURES
'---------------------------------------------------------------------------------------------------
'USER-DEFINED TYPES:
    TYPE COMMAND
        Operation AS STRING * 25    'The keyword for the operation to be performed.
        Path AS STRING * 255        'The path where an input file (include, append, etc.) may be found.
        Find AS STRING * 255        'The sub-string to be replaced by the "Add" sub-string.
        Add AS STRING * 255         'The sub-string to added to the main string.
        Precedant AS STRING * 255   'The sub-string that marks the beginning of an affected area in the main string.
        Antecedant AS STRING * 255  'The sub-string that marks the end of an affected area in the main string.
    END TYPE
    TYPE TOKEN
        Token AS STRING * 10        'The stand-in for a string of characters.
        Characters AS STRING * 25   'The string of characters represented by the stand-in.
    END TYPE
'---------------------------------------------------------------------------------------------------
'GLOBAL CONSTANTS:
    CONST TRUE = -1 'Allows integers to be "TRUE" (compability with other BASIC dialects).
    CONST FALSE = 0 'Allows integers to be "FALSE" (compability with other BASIC dialects).
'---------------------------------------------------------------------------------------------------
'GLOBAL VARIABLES:
    DIM strCommandLineParameter AS STRING   'The command-line parameters that TTL was run with.
    DIM strScriptPath AS STRING             'The path of the script (TTL algorithm) to be run.
    DIM strInputPath AS STRING              'The path of the input file the script will run against.
    DIM strOutputPath AS STRING             'The path where TTL should put the finished text.
    DIM udtCommands AS COMMAND              'The dynamic array of [Commands] that will be assembled and executed.
    DIM strInputText as STRING              'The input text the script will run against.
'---------------------------------------------------------------------------------------------------
'MAIN PROCEDURE:
    'INITIALIZE:
        REDIM udtCommands(0) AS COMMAND
    'INPUT:
        'Input the command-line parameters:
            strCommandLineParameter = COMMAND$
    'PROCESSING:
        'Parse the script path out of the command-line parameters:
            strScriptPath = LTRIM$(RTRIM$(Before$(strCommandLineParameter, ",", 1)))
        'Parse the input path out of the command-line parameters:
            strInputPath = LTRIM$(RTRIM$(Between$(strCommandLineParameter, ",", ",", 1)))
        'Parse the output path out of the command-line parameters:
            strOutputPath = LTRIM$(RTRIM$(After$(After$(strCommandLineParameter, strInputPath, 1), ",", 1)))
        'Load the input text:
            Print "LOADING FILE>>>"
            strInputText = Input_File$(strInputPath)
        'Parse the script:
            Print "PARSING FILE>>>"
            CALL Parse_Script(udtCommands(), strScriptPath)
        'Execute the script:
            Print "EXECUTING SCRIPT>>>"
            strInputText = Execute_Script$(udtCommands(), strInputText)
        'Output the output text:
            Print "OUTPUTTING FILE>>>"
            CALL Output_File(strInputText, strOutputPath)
    END

'---------------------------------------------------------------------------------------------------
'Parse
'---------------------------------------------------------------------------------------------------
SUB Parse_Script (Commands() AS COMMAND, Script_Path AS STRING)
    'SUMMARY:
        '[Parse_Script] .
    'INPUT:
        'Commands():
        'Script_Path:
    'VARIABLES:
        DIM intCommand AS INTEGER           'The address of the current [Command] within the [Commands] array.
        DIM Temporary_Commands AS COMMAND
        DIM strFileText AS STRING
        DIM strScriptText() AS STRING
    'INITIALIZE:
        REDIM Temporary_Commands(0) AS COMMAND
        REDIM strScriptText(0) AS STRING
        intCommand = LBOUND(Commands)
    'PROCESSING:
        'Load the script file:
            strFileText = Input_File$(Script_Path)
            CALL Seperate_Lines(strScriptText(), strFileText, CHR$(13)+CHR$(10))
        'Load the script:
            CALL Load_Script(strScriptText(), Commands())
        'While there are more commands to add...
            WHILE intCommand <= UBOUND(Commands)
                'INITIALIZE: the temporary command
                    REDIM Temporary_Commands(0) AS COMMAND
                'If the current command is "Include..."
                    IF LTRIM$(RTRIM$(Commands(intCommand).Operation)) = "INCLUDE" THEN
                        'Load the script file:
                            strFileText = Input_File$(Between$(Commands(intCommand).Path, CHR$(34), CHR$(34), 1))
                            CALL Seperate_Lines(strScriptText(), strFileText, CHR$(13)+CHR$(10))
                        'Load the script:
                            CALL Load_Script(strScriptText(), Temporary_Commands())
                        'Add the commands to the script:
                            CALL Add_Commands(Commands(), Temporary_Commands(), intCommand)
                    ELSE
                        'Increment the current command:
                            intCommand = intCommand + 1
                    END IF
        'Next [command]...
            WEND
END SUB

SUB Load_Script (Words() AS STRING, Commands() AS COMMAND)
    'SUMMARY:
        '[Load_Script] .
    'INPUT:
        'Words():
        'Commands():
    'VARIABLES:
        'DIM strCommands AS STRING
        DIM intLine AS INTEGER      'The address of the current [Line] within the [Script] array.
    'INITIALIZE:
        'REDIM strCommands(0) AS STRING
    'PROCESSING:
        'If the file contains text...
            IF (UBOUND(Words) > 0) OR (LEN(Words(0)) > 0) THEN
                'For each [line]...
                    FOR intLine = LBOUND(Words) TO UBOUND(Words)
                        'If the line is not blank...
                            IF Words(intLine) <> "" THEN
                                'Parse the command:
                                    CALL Parse_Command(Commands(), Words(intLine))
                            END IF
                'Next [line]...
                    NEXT intLine
            ELSE
                PRINT "File Error 0001: The file is blank."
                END
            END IF
END SUB

SUB Parse_Command (Commands() AS COMMAND, Text_Command AS STRING)
    'SUMMARY:
        '[Parse_Command] .
    'INPUT:
        'Commands():
        'Text_Command:
    'VARIABLES:
        DIM strWords AS STRING
        DIM udtTemporaryCommand AS COMMAND
    'INITIALIZE:
        CALL Initialize_Command(udtTemporaryCommand)
        REDIM strWords(0) AS STRING
    'PROCESSING:
        'Separate the words in the command:
            CALL Seperate_Words(strWords(), Text_Command)
        'If there are words in the command...
            IF strWords(0) <> "" THEN
                'Intepret the command
                    CALL Intepret_Command(udtTemporaryCommand, strWords())
            END IF
        'If the command contains instructions...
            IF LEFT$(udtTemporaryCommand.Operation, 1) <> " " THEN
                'If the first command is blank...
                    IF Filter$(Commands(0).Operation, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") = "" THEN
                        'Add the command to the first position in the array:
                            Commands(0) = udtTemporaryCommand
                    ELSE
                        'Expand the array:
                            REDIM _PRESERVE Commands(UBOUND(Commands) + 1) AS COMMAND
                        'Add the command to the last position in the array:
                            Commands(UBOUND(Commands)) = udtTemporaryCommand
                    END IF
            END IF
END SUB

SUB Seperate_Words (Words() AS STRING, Text_Command AS STRING)
    'SUMMARY:
        '[Seperate_Words] .
    'INPUT:
        'Words():
        'Text_Command:
    'CONSTANTS:
        CONST ALLOWED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()'/+&"
    'VARIABLES:
        DIM intCharacter AS INTEGER     'The address of the current character in the [Text].
        DIM strCharacter AS STRING * 1  'The character being examined.
        DIM bolQuote AS INTEGER         'Whether there is an open quotation mark.
        DIM intWord AS INTEGER          'The [Word] within the [Command Words] array.
    'INITIALIZE:
        intCharacter = 1
        bolQuote = FALSE
        intWord = 0
        REDIM Words(0) AS STRING
    'PROCESSING:
        'While there are more characters to process...
            WHILE (intCharacter <= LEN(Text_Command))
                'Pull the character:
                    strCharacter = MID$(Text_Command, intCharacter, 1)
                'If the character is allowed...
                    IF INSTR(1, ALLOWED, strCharacter) THEN
                        'Add [Character] to the current [word]:
                            Words(intWord) = Words(intWord) + strCharacter
                    ELSE
                        'If the character is a quote...
                            IF strCharacter = CHR$(34) THEN
                                'If we are in an active quote...
                                    IF bolQuote = TRUE THEN
                                        'End quote:
                                            Words(intWord) = Words(intWord) + CHR$(34)
                                        'Start a new word:
                                            intWord = intWord + 1
                                        'Start a new word:
                                            REDIM _PRESERVE Words(intWord) AS STRING
                                        'Start a new word:
                                            bolQuote = FALSE
                                    ELSE
                                        'If the current word is not blank...
                                            IF Words(intWord) <> "" THEN
                                                'Start a new word:
                                                    intWord = intWord + 1
                                                'Start a new word:
                                                    REDIM _PRESERVE Words(intWord) AS STRING
                                            END IF
                                        Words(intWord) = CHR$(34)
                                        bolQuote = TRUE
                                    END IF
                            ELSEIF bolQuote = TRUE THEN
                                'Add [Character] to the current [word]:
                                    Words(intWord) = Words(intWord) + strCharacter
                            ELSE
                            'If the current word is not blank...
                                IF Words(intWord) <> "" THEN
                                    'Start a new word:
                                        intWord = intWord + 1
                                    'Start a new word:
                                        REDIM _PRESERVE Words(intWord) AS STRING
                                END IF
                            END IF
                    END IF
                'Increment character:
                intCharacter = intCharacter + 1
        'Next [Character]...
            WEND
        'If the last word is blank...
            IF Words(intWord) = "" THEN
                'Remove the last word:
                    intWord = intWord - 1
                'Remove the last word:
                    REDIM _PRESERVE Words(intWord) AS STRING
            END IF
END SUB

SUB Initialize_Command (Temporary_Command AS COMMAND)
    'SUMMARY:
        '[Initialize_Command] .
    'INPUT:
        'Temporary_Command:
    'INITIALIZE:
        Temporary_Command.Operation = ""
        Temporary_Command.Path = ""
        Temporary_Command.Add = ""
        Temporary_Command.Find = ""
        Temporary_Command.Precedant = ""
        Temporary_Command.Antecedant = ""
END SUB

SUB Intepret_Command (Temporary_Command AS COMMAND, Command_Words() AS STRING)
    'SUMMARY:
        '[Intepret_Command] .
    'INPUT:
        'Temporary_Command:
        'Command_Words():
    'VARIABLES:
        DIM intWord AS INTEGER      'The [Word] within the [Command Words] array.
        DIM intNextWord AS INTEGER  'The next [Word] within the [Command Words] array.
        DIM intCharacter AS INTEGER 'The address of the current character in the [Text].
        DIM strTemporary AS STRING  'The temporary string that holds the output string as it is assembled.
    'PROCESSING:
        'Normalize commands:
            CALL Normalize_Commands(Command_Words())
        'Remove comments:
            CALL Remove_Comments(Command_Words())
        'If the command words are not blank...
            IF UBOUND(Command_Words) > 0 OR Command_Words(0) <> "" THEN
                'Remove blank words:
                    CALL Remove_Blank_Words(Command_Words())
                'Transform CHR commands to their character:
                    CALL Transform_CHR(Command_Words())
                'Combine all sub-strings with a "+" or a "&" in between:
                    CALL Combine_Sub_Strings(Command_Words())
                'Remove blank words:
                    CALL Remove_Blank_Words(Command_Words())
                'Validate all words:
                    CALL Validate_Command(Command_Words())
                'Add [command words] to [temporary command]:
                    CALL Populate_Command(Command_Words(), Temporary_Command)
            END IF
END SUB

SUB Normalize_Commands (Words() AS STRING)
    'SUMMARY:
        '[Normalize_Commands] .
    'INPUT:
        'Words(): 
    'VARIABLES:
        DIM intWord AS INTEGER  'The [Word] within the [Command Words] array.
    'PROCESSING:
        'For each [word]...
            FOR intWord = LBOUND(Words) TO UBOUND(Words)
                'If the word begins and ends in something other than quotes...
                    IF LEFT$(Words(intWord), 1) <> CHR$(34) AND RIGHT$(Words(intWord), 1) <> CHR$(34) THEN
                        'Upper-case the word:
                            Words(intWord) = UCASE$(Words(intWord))
                    END IF
        'Next [word]...
            NEXT intWord
END SUB

SUB Remove_Comments (Words() AS STRING)
    'SUMMARY:
        '[Remove_Comments] .
    'INPUT:
        'Words():
    'VARIABLES:
        DIM intWord AS INTEGER  'The [Word] within the [Command Words] array.
    'PROCESSING:
        'For each [word]...
            FOR intWord = UBOUND(Words) TO LBOUND(Words) STEP -1
                'If the word begins and ends in something other than quotes...
                    IF LEFT$(Words(intWord), 1) <> CHR$(34) AND RIGHT$(Words(intWord), 1) <> CHR$(34) THEN
                        'If the word is not blank...
                            IF Words(intWord) <> "" THEN
                                'If the [word] contains "'"...
                                    IF INSTR(1, Words(intWord), "'") THEN
                                        'Remove characters after "'":
                                            Words(intWord) = Before$(Words(intWord), "'", 1)
                                        'Remove subsequent words:
                                            REDIM _PRESERVE Words(intWord) AS STRING
                                    END IF
                            END IF
                        'If the word is not blank...
                            IF Words(intWord) <> "" THEN
                                'If the [word] contains "//"...
                                    IF INSTR(1, Words(intWord), "//") THEN
                                        'Remove characters after "//":
                                            Words(intWord) = Before$(Words(intWord), "//", 1)
                                        'Remove subsequent words:
                                            REDIM _PRESERVE Words(intWord) AS STRING
                                    END IF
                            END IF
                        'If the word is not blank...
                            IF Words(intWord) <> "" THEN
                                'If the [word] contains "REM"...
                                    IF INSTR(1, Words(intWord), "REM") THEN
                                    'Remove characters after "REM":
                                        Words(intWord) = Before$(Words(intWord), "REM", 1)
                                    'Remove subsequent words:
                                        REDIM _PRESERVE Words(intWord) AS STRING
                                    END IF
                            END IF
                    END IF
        'Next [word]...
            NEXT intWord
END SUB

SUB Transform_CHR (Command_Words() AS STRING)
    'SUMMARY:
        '[Transform_CHR] .
    'INPUT:
        'Command_Words():
    'VARIABLES:
        DIM intWord AS INTEGER 'The [Word] within the [Command Words] array.
        DIM intCharacter AS INTEGER 'The address of the current character in the [Text].
        DIM strTemporary AS STRING 'The temporary string that holds the output string as it is assembled.
    'PROCESSING:
        'For each [word]...
            FOR intWord = LBOUND(Command_Words) TO UBOUND(Command_Words)
                'If the word begins and ends in something other than quotes...
                    IF LEFT$(Command_Words(intWord), 1) <> CHR$(34) AND RIGHT$(Command_Words(intWord), 1) <> CHR$(34) THEN
                        'If the "CHR" command appears...
                            IF INSTR(1, Command_Words(intWord), "CHR") THEN
                                'If the "CHR" command is formatted as "CHR(#)"...
                                    IF LEFT$(Command_Words(intWord), 4) = "CHR(" AND RIGHT$(Command_Words(intWord), 1) = ")" THEN
                                        'Extract the number from the command:
                                            strTemporary = MID$(Command_Words(intWord), 5, LEN(Command_Words(intWord)) - 5)
                                        'Filter all non-numeric characters:
                                            strTemporary = Filter$(strTemporary, "0123456789")
                                        'If the temporary string is between 1 and 3 characters...
                                            IF LEN(strTemporary) > 0 AND LEN(strTemporary) < 4 THEN
                                                'Determine the numeric value of the string:
                                                    intCharacter = CINT(VAL(strTemporary))
                                                'Replace the word with the appropriate ASCII character:
                                                    Command_Words(intWord) = CHR$(34) + CHR$(intCharacter) + CHR$(34)
                                            ELSE
                                                PRINT "Syntax Error 0001: The CHR() number is out of range."
                                                END
                                            END IF
                                    ELSE
                                        PRINT "Syntax Error 0000: The CHR() command is not correctly formatted."
                                        PRINT Command_Words(intWord)
                                        END
                                    END IF
                            END IF
                    END IF
        'Next [word]...
            NEXT intWord
END SUB

SUB Combine_Sub_Strings (Command_Words() AS STRING)
    'SUMMARY:
        '[Combine_Sub_Strings] .
    'INPUT:
        'Command_Words():
    'VARIABLES:
        DIM intWord AS INTEGER  'The [Word] within the [Command Words] array.
    'INITIALIZE:
        intWord = LBOUND(Command_Words) + 1
    'PROCESSING:
        'While there are more words to process...
            WHILE intWord < UBOUND(Command_Words)
                'If the word is a "+" or "&"...
                    IF (Command_Words(intWord) = "+") OR (Command_Words(intWord) = "&") THEN
                        'If the preceeding word begins and ends with quotes...
                            IF (LEFT$(Command_Words(intWord - 1), 1) = CHR$(34)) AND (RIGHT$(Command_Words(intWord - 1), 1) = CHR$(34)) THEN
                                'If the following word begins and ends with quotes...
                                    IF LEFT$(Command_Words(intWord + 1), 1) = CHR$(34) AND RIGHT$(Command_Words(intWord + 1), 1) = CHR$(34) THEN
                                        'Join the following word into the preceeding word:
                                            Command_Words(intWord - 1) = LEFT$(Command_Words(intWord - 1), LEN(Command_Words(intWord - 1)) - 1) + RIGHT$(Command_Words(intWord + 1), LEN(Command_Words(intWord + 1)) - 1)
                                        'Blank the following word:
                                            Command_Words(intWord + 1) = ""
                                        'Blank the "+" or "&" operator:
                                            Command_Words(intWord) = ""
                                        'Remove blank words:
                                            CALL Remove_Blank_Words(Command_Words())
                                    ELSE
                                        PRINT "Syntax Error: " + Command_Words(intWord) + " should be used to join strings."
                                        END
                                END IF
                            ELSE
                                PRINT "Syntax Error: " + Command_Words(intWord) + " should be used to join strings."
                                END
                            END IF
                    ELSE
                        intWord = intWord + 1
                    END IF
        'Next [word]...
            WEND
END SUB

SUB Validate_Command (Words() AS STRING)
    'SUMMARY:
        '[Validate_Command] .
    'INPUT:
        'Words():
    'VARIABLES:
        DIM intWord AS INTEGER  'The [Word] within the [Command Words] array.
    'PROCESSING:
        'For each [word]...
            FOR intWord = LBOUND(Words) TO UBOUND(Words)
                'If the word is not a substring...
                IF RIGHT$(Words(intWord), 1) <> CHR$(34) OR LEFT$(Words(intWord), 1) <> CHR$(34) THEN
                    SELECT CASE Words(intWord)
                        CASE "REPLACE"
                        CASE "WITH"
                        CASE "FROM"
                        CASE "AND"
                        CASE "ALL"
                        CASE "BETWEEN"
                        CASE "INCLUDE"
                        CASE "EVERYTHING"
                        CASE "AFTER"
                        CASE "BEFORE"
                        CASE "NEXT"
                        CASE "TO"
                        CASE ELSE
                            PRINT "Error 0000: Unrecognized word " + Words(intWord)
                            END
                    END SELECT
                END IF
        'Next [word]...
            NEXT intWord
END SUB

SUB Populate_Command (Command_Words() AS STRING, Temporary_Command AS COMMAND)
    'SUMMARY:
        '[Populate_Command] .
    'INPUT:
        'Command_Words():
        'Temporary_Command:
    'PROCESSING:
        SELECT CASE UBOUND(Command_Words)
            CASE 0
                PRINT "Error 0000: Incomplete command " + Command_Words(0)
                END
            CASE 1
                IF Command_Words(0) = "INCLUDE" AND Substring%(Command_Words(1)) = TRUE THEN
                    Temporary_Command.Operation = "INCLUDE"
                    Temporary_Command.Path = Command_Words(1)
                ELSE
                    PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1)
                    END
                END IF
            CASE 2
                PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1) + " " + Command_Words(2)
                END
            CASE 3
                IF Command_Words(0) = "REPLACE" AND Substring%(Command_Words(1)) = TRUE AND Command_Words(2) = "WITH" AND Substring%(Command_Words(3)) = TRUE THEN
                    Temporary_Command.Operation = "REPLACE"
                    Temporary_Command.Find = Command_Words(1)
                    Temporary_Command.Add = Command_Words(3)
                ELSE
                    PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1) + " " + Command_Words(2) + " " + Command_Words(3)
                    END
                END IF
            CASE 4
                PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1) + " " + Command_Words(2) + " " + Command_Words(3) + " " + Command_Words(4)
                END
            CASE 5
                PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1) + " " + Command_Words(2) + " " + Command_Words(3) + " " + Command_Words(4) + " " + Command_Words(5)
                END
            CASE 6
                PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1) + " " + Command_Words(2) + " " + Command_Words(3) + " " + Command_Words(4) + " " + Command_Words(5) + " " + Command_Words(6)
                END
            CASE 7
                IF Command_Words(0) = "REPLACE" AND Command_Words(1) = "ALL" AND Command_Words(2) = "WITH" AND Substring%(Command_Words(3)) = TRUE AND Command_Words(4) = "BETWEEN" AND Substring%(Command_Words(5)) = TRUE AND Command_Words(6) = "AND" AND Substring%(Command_Words(7)) = TRUE THEN
                    Temporary_Command.Operation = "REPLACEBETWEEN"
                    Temporary_Command.Add = Command_Words(3)
                    Temporary_Command.Precedant = Command_Words(5)
                    Temporary_Command.Antecedant = Command_Words(7)
                ELSEIF Command_Words(0) = "REPLACE" AND Substring%(Command_Words(1)) = TRUE AND Command_Words(2) = "WITH" AND Substring%(Command_Words(3)) = TRUE AND Command_Words(4) = "BETWEEN" AND Substring%(Command_Words(5)) = TRUE AND Command_Words(6) = "AND" AND Substring%(Command_Words(7)) = TRUE THEN
                    Temporary_Command.Operation = "REPLACEIFBETWEEN"
                    Temporary_Command.Find = Command_Words(1)
                    Temporary_Command.Add = Command_Words(3)
                    Temporary_Command.Precedant = Command_Words(5)
                    Temporary_Command.Antecedant = Command_Words(7)
                ELSEIF Command_Words(0) = "REPLACE" AND Command_Words(1) = "ALL" AND Command_Words(2) = "WITH" AND Substring%(Command_Words(3)) = TRUE AND Command_Words(4) = "FROM" AND Substring%(Command_Words(5)) = TRUE AND Command_Words(6) = "AND" AND Substring%(Command_Words(7)) = TRUE THEN
                    Temporary_Command.Operation = "REPLACEFROM"
                    Temporary_Command.Add = Command_Words(3)
                    Temporary_Command.Precedant = Command_Words(5)
                    Temporary_Command.Antecedant = Command_Words(7)
                ELSEIF Command_Words(0) = "REPLACE" AND Command_Words(1) = "EVERYTHING" AND Command_Words(2) = "AFTER" AND Substring%(Command_Words(3)) = TRUE AND Command_Words(4) = "TO" AND Substring%(Command_Words(5)) = TRUE AND Command_Words(6) = "WITH" AND Substring%(Command_Words(7)) = TRUE THEN
                    Temporary_Command.Operation = "REPLACESUBSEQUENT"
                    Temporary_Command.Add = Command_Words(7)
                    Temporary_Command.Precedant = Command_Words(3)
                    Temporary_Command.Antecedant = Command_Words(5)
                ELSE
                    PRINT "Error 0000: Unrecognized Command " + Command_Words(0) + " " + Command_Words(1) + " " + Command_Words(2) + " " + Command_Words(3) + " " + Command_Words(4) + " " + Command_Words(5) + " " + Command_Words(6) + " " + Command_Words(7)
                    END
                END IF
            CASE ELSE
                PRINT "Error 0000: Unrecognized Command"
                END
        END SELECT
END SUB

FUNCTION Substring% (Text AS STRING)
    'SUMMARY:
        '[Substring%] .
    'INPUT:
        'Text:
    'PROCESSING:
        'If the first and last characters are quotes...
            IF LEFT$(Text, 1) = CHR$(34) AND RIGHT$(Text, 1) = CHR$(34) THEN
                Substring% = TRUE
            ELSE
                Substring% = FALSE
            END IF
END SUB

SUB Add_Commands (Main_Commands() AS COMMAND, New_Commands() AS COMMAND, Line_Number AS INTEGER)
    'SUMMARY:
        '[Add_Commands] .
    'INPUT:
        'Main_Commands():
        'New_Commands():
        'Line_Number:
    'VARIABLES:
        DIM intCommand AS INTEGER   'The address of the [Command] within the [Commands] array.
    'PROCESSING:
        'If there is a command populated...
            IF UBOUND(New_Commands) > 0 OR LEFT$(New_Commands(0).Operation, 1) <> " " THEN
                'Resize the main array:
                    REDIM _PRESERVE Main_Commands(UBOUND(Main_Commands) + UBOUND(New_Commands)) AS COMMAND
                'Move the items in the main array down:
                    FOR intCommand = UBOUND(Main_Commands) TO (Line_Number + UBOUND(New_Commands) + 1) STEP -1
                        Main_Commands(intCommand) = Main_Commands(intCommand - UBOUND(New_Commands))
                    NEXT intCommand
                'Add the new commands to the command structure:
                    FOR intCommand = LBOUND(New_Commands) TO UBOUND(New_Commands)
                        Main_Commands(Line_Number + intCommand) = New_Commands(intCommand)
                    NEXT intCommand
            END IF
END SUB

'---------------------------------------------------------------------------------------------------
'Execute
'---------------------------------------------------------------------------------------------------
FUNCTION Execute_Script$ (Commands() AS COMMAND, Text as String)
    'SUMMARY:
        '[Execute_Script$] .
    'INPUT:
        'Commands(): 
        'Text: The input string; the text that's being manipulated.
    'VARIABLES:
        DIM intCommand AS INTEGER   'The address of the current [Command] within the [Commands] array.
    'PROCESSING:
        'For each [command]...
            FOR intCommand = LBOUND(Commands) TO UBOUND(Commands)
                'Execute the command:
                    CALL Execute_Command(Commands(intCommand), Text)
        'Next [command]...
            NEXT intCommand
    'OUTPUT:
        Execute_Script$ = Text
END FUNCTION

SUB Execute_Command (Command AS COMMAND, Text AS STRING)
    'SUMMARY:
       '[Execute_Command] .
    'INPUT:
        'Command: 
        'Text: The input string; the text that's being manipulated.
    'PROCESSING:
        SELECT CASE LTRIM$(RTRIM$(Command.Operation))
            CASE "REPLACE"
                Text = Replace$(Text, Interior_Characters$(Command.Find), Interior_Characters$(Command.Add))
            CASE "REPLACEBETWEEN"
                Text = Replace_Between$(Text, Interior_Characters$(Command.Precedant), Interior_Characters$(Command.Antecedant), Interior_Characters$(Command.Add))
            CASE "REPLACEIFBETWEEN"
                Text = Replace_If_Between$(Text, Interior_Characters$(Command.Precedant), Interior_Characters$(Command.Antecedant), Interior_Characters$(Command.Find), Interior_Characters$(Command.Add))
            CASE "REPLACEFROM"
                Text = Replace_From$(Text, Interior_Characters$(Command.Precedant), Interior_Characters$(Command.Antecedant), Interior_Characters$(Command.Add))
            CASE "REPLACESUBSEQUENT"
                Text = Replace_Subsequent$ (Text, Interior_Characters$(Command.Precedant), Interior_Characters$(Command.Antecedant), Interior_Characters$(Command.Add))
        END SELECT
END SUB

FUNCTION Interior_Characters$(Text AS STRING)
  Text = LTRIM$(RTRIM$(Text))
  Text = MID$(Text, 2, LEN(Text) - 2)
  Interior_Characters$ = Text
END FUNCTION

'$INCLUDE: 'string.array.bas'
'$INCLUDE: 'text.file.input.output.bas'
'$INCLUDE: 'string.manipulation.bas'